package com.game.login.netty;

import com.game.framework.common.error.TokenException;
import com.game.framework.common.model.AccountToken;
import com.game.framework.common.utils.JWTUtil;
import com.game.framework.message.protobuf.ActivityProto;
import com.game.framework.message.protobuf.BaseProto;
import com.game.framework.message.protobuf.UserProto;
import com.game.login.service.ActivityService;
import com.game.login.service.LoginService;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import io.jsonwebtoken.ExpiredJwtException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ObjectUtils;

@Slf4j
public class ClientNettyHandler extends ChannelInboundHandlerAdapter {
    public static final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    protected AccountToken accountToken;
    private final LoginService loginService;
    private final ActivityService activityService;
    ClientNettyHandler(LoginService loginService, ActivityService activityService) {
        this.loginService = loginService;
        this.activityService = activityService;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws InvalidProtocolBufferException {
        ByteString resBody = null;
        BaseProto.ErrorCode errorCode = BaseProto.ErrorCode.SUCCESS;
        BaseProto.BaseReq baseReq = (BaseProto.BaseReq) msg;
        log.info("收到消息: " + baseReq);
        String token = baseReq.getToken();
        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_HEARTBEAT)) {
            UserProto.HeartbeatRequest request = UserProto.HeartbeatRequest.parseFrom(baseReq.getBody());
            log.info("【MC_HEARTBEAT】请求消息：" + request);
            UserProto.HeartbeatResponse.Builder response = UserProto.HeartbeatResponse.newBuilder()
                    .setTime(System.currentTimeMillis());
            resBody = response.build().toByteString();
        }

        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_LOGIN)) {
            UserProto.LoginRequest request = UserProto.LoginRequest.parseFrom(baseReq.getBody());
            log.info("【MC_LOGIN】请求消息：" + request);
            UserProto.LoginResponse.Builder response = UserProto.LoginResponse.newBuilder();
            errorCode = loginService.login(request, response);
            // 加入连接管理
            if (errorCode.getNumber() == BaseProto.ErrorCode.SUCCESS.getNumber()) {
                resBody = response.build().toByteString();
                //if(clientChannelService == null){
                //    clientChannelService = applicationContext.getBean(ClientChannelService.class);
                //}
                //clientChannelService.addChannel(response.getUser().getUserId(), ctx.channel());
            } else {
                resBody = UserProto.LoginResponse.newBuilder().build().toByteString();
            }
        }

        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_CREATE_ROLE)) {
            if(checkUserToken(token, baseReq.getMc(), ctx)){
                UserProto.CreateRoleRequest request = UserProto.CreateRoleRequest.parseFrom(baseReq.getBody());
                log.info("【MC_CREATE_PLAYER】请求消息：" + request);
                UserProto.CreateRoleResponse.Builder response = UserProto.CreateRoleResponse.newBuilder();
                errorCode = loginService.createRole(request, response);
                resBody = response.build().toByteString();
            }
        }

        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_UPDATE_ROLE)) {
            if(checkUserToken(token, baseReq.getMc(), ctx)) {
                UserProto.UpdateRoleRequest request = UserProto.UpdateRoleRequest.parseFrom(baseReq.getBody());
                log.info("【MC_UPDATE_PLAYER】请求消息：" + request);
                UserProto.UpdateRoleResponse.Builder response = UserProto.UpdateRoleResponse.newBuilder();
                errorCode = loginService.updateRole(request, response);
                resBody = response.build().toByteString();
            }
        }

        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_GET_USER_INFO)) {
            if(checkUserToken(token, baseReq.getMc(), ctx)) {
                UserProto.GetUserInfoRequest request = UserProto.GetUserInfoRequest.parseFrom(baseReq.getBody());
                log.info("【MC_GET_USER_INFO】请求消息：" + request);
                UserProto.GetUserInfoResponse.Builder response = UserProto.GetUserInfoResponse.newBuilder();
                errorCode = loginService.getUserInfo(request, response);
                resBody = response.build().toByteString();
            }
        }

        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_GET_ROLE_INFO)) {
            if(checkUserToken(token, baseReq.getMc(), ctx)) {
                UserProto.GetRoleInfoRequest request = UserProto.GetRoleInfoRequest.parseFrom(baseReq.getBody());
                log.info("【MC_GET_ROLE_INFO】请求消息：" + request);
                UserProto.GetRoleInfoResponse.Builder response = UserProto.GetRoleInfoResponse.newBuilder();
                errorCode = loginService.getRoleInfo(request, response);
                resBody = response.build().toByteString();
            }
        }

        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_ENTER_GAME)) {
            if(checkUserToken(token, baseReq.getMc(), ctx)) {
                UserProto.EnterGameRequest request = UserProto.EnterGameRequest.parseFrom(baseReq.getBody());
                log.info("【MC_ENTER_GAME】请求消息：" + request);
                UserProto.EnterGameResponse.Builder response = UserProto.EnterGameResponse.newBuilder();
                errorCode = loginService.enterGame(request, response);
                resBody = response.build().toByteString();
            }
        }

        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_EXIT_GAME)) {
            if(checkUserToken(token, baseReq.getMc(), ctx)) {
                UserProto.ExitGameRequest request = UserProto.ExitGameRequest.parseFrom(baseReq.getBody());
                log.info("【MC_EXIT_GAME】请求消息：" + request);
                UserProto.ExitGameResponse.Builder response = UserProto.ExitGameResponse.newBuilder();
                errorCode = loginService.exitGame(request, response);
                resBody = response.build().toByteString();
            }
        }

        if (baseReq.getMc().equals(BaseProto.MessageCode.MC_GET_ACTIVITY_LIST)) {
            if(checkUserToken(token, baseReq.getMc(), ctx)) {
                ActivityProto.GetActivityListRequest request = ActivityProto.GetActivityListRequest.parseFrom(baseReq.getBody());
                log.info("【MC_GET_ACTIVITY_LIST】请求消息：" + request);
                ActivityProto.GetActivityListResponse.Builder response = ActivityProto.GetActivityListResponse.newBuilder();
                errorCode = activityService.getActivityList(request, response);
                resBody = response.build().toByteString();
            }
        }
        if (!ObjectUtils.isEmpty(resBody)) {
            sendMessage(
                    baseReq.getMc(),
                    BaseProto.ResStatus.STATUS_SUCCESS,
                    errorCode,
                    resBody,
                    ctx);
        } else {
            sendMessage(
                    baseReq.getMc(),
                    BaseProto.ResStatus.STATUS_ERROR,
                    errorCode,
                    ByteString.EMPTY,
                    ctx);
        }
        log.info("返回结果：" + resBody);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        log.info("新的客户端链接：" + ctx.channel().id().asShortText());
        clients.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        log.info("客户端断开链接：" + ctx.channel().id().asShortText());
        clients.remove(ctx.channel());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.info("客户端发生错误：" + ctx.channel().id().asShortText() + "  " + cause.getStackTrace().toString());
        ctx.channel().close();
        clients.remove(ctx.channel());
    }

    private void sendMessage(BaseProto.MessageCode mc, BaseProto.ResStatus status, BaseProto.ErrorCode errorCode, ByteString body, ChannelHandlerContext ctx) {
        BaseProto.BaseRes baseRes = BaseProto.BaseRes.newBuilder()
                .setMc(mc)
                .setStatus(status)
                .setCode(errorCode)
                .setBody(body)
                .build();
        ByteBuf response = Unpooled.copiedBuffer(baseRes.toByteArray());
        ctx.writeAndFlush(response);
    }

    public Boolean checkUserToken(String token, BaseProto.MessageCode mc,  ChannelHandlerContext ctx) {
        // 解析token里面的内容，如果解析失败，会抛出异常
        try {
            if(StringUtils.isNotEmpty(token)) {
                accountToken = JWTUtil.getTokenContent(token, AccountToken.class);
            }else {
                sendMessage(
                        mc,
                        BaseProto.ResStatus.STATUS_ERROR,
                        BaseProto.ErrorCode.TOKEN_ILLEGAL,
                        ByteString.EMPTY,
                        ctx);
                ctx.close();
                log.error("token解析异常，直接关闭连接");
                return false;
            }
        }catch (ExpiredJwtException | TokenException e){
            // 告诉客户端token过期，它客户端重新获取并重新连接
            if (e instanceof ExpiredJwtException) {
                sendMessage(
                        mc,
                        BaseProto.ResStatus.STATUS_ERROR,
                        BaseProto.ErrorCode.TOKEN_EXPIRE,
                        ByteString.EMPTY,
                        ctx);
                ctx.close();
                log.warn("token过期，关闭连接");
            } else {
                sendMessage(
                        mc,
                        BaseProto.ResStatus.STATUS_ERROR,
                        BaseProto.ErrorCode.TOKEN_ILLEGAL,
                        ByteString.EMPTY,
                        ctx);
                ctx.close();
                log.error("token解析异常，直接关闭连接", e);
            }
            return false;
        }
        return true;
    }
}